import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
import { createTestingModule } from './setup';
import { Repository } from 'typeorm';
import { getRepositoryToken } from '@nestjs/typeorm';
import { User } from '../src/database/entities/user.entity';
import { Role } from '../src/database/entities/role.entity';
import * as bcrypt from 'bcrypt';

describe('AuthController (e2e)', () => {
  let app: INestApplication;
  let userRepository: Repository<User>;
  let roleRepository: Repository<Role>;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await createTestingModule([
      AppModule,
    ]);

    app = moduleFixture.createNestApplication();
    await app.init();

    userRepository = moduleFixture.get<Repository<User>>(getRepositoryToken(User));
    roleRepository = moduleFixture.get<Repository<Role>>(getRepositoryToken(Role));

    // 创建默认角色
    const adminRole = roleRepository.create({
      name: 'admin',
      code: 'admin',
      description: '系统管理员',
      status: 1,
      sort: 0,
    });

    const userRole = roleRepository.create({
      name: 'user',
      code: 'user',
      description: '普通用户',
      status: 1,
      sort: 1,
    });

    await roleRepository.save([adminRole, userRole]);
  });

  afterEach(async () => {
    await app.close();
  });

  describe('/api/auth/register (POST)', () => {
    it('should register a new user successfully', async () => {
      const registerDto = {
        username: 'testuser',
        email: 'test@example.com',
        password: 'password123',
        nickname: 'Test User',
        phone: '13800138000',
      };

      const response = await request(app.getHttpServer())
        .post('/api/auth/register')
        .send(registerDto)
        .expect(201);

      expect(response.body).toMatchObject({
        id: expect.any(Number),
        username: registerDto.username,
        email: registerDto.email,
        nickname: registerDto.nickname,
        phone: registerDto.phone,
        createTime: expect.any(String),
      });

      expect(response.body).not.toHaveProperty('password');
    });

    it('should return 409 when username already exists', async () => {
      // 先创建一个用户
      const existingUser = userRepository.create({
        username: 'existinguser',
        email: 'existing@example.com',
        password: await bcrypt.hash('password123', 12),
        nickname: 'Existing User',
        phone: '13800138001',
        status: 1,
      });
      await userRepository.save(existingUser);

      const registerDto = {
        username: 'existinguser', // 使用已存在的用户名
        email: 'new@example.com',
        password: 'password123',
        nickname: 'New User',
        phone: '13800138002',
      };

      const response = await request(app.getHttpServer())
        .post('/api/auth/register')
        .send(registerDto)
        .expect(409);

      expect(response.body.message).toContain('用户名已存在');
    });

    it('should return 400 when required fields are missing', async () => {
      const invalidDto = {
        username: 'testuser',
        // 缺少 email, password 等必填字段
      };

      await request(app.getHttpServer())
        .post('/api/auth/register')
        .send(invalidDto)
        .expect(400);
    });
  });

  describe('/api/auth/login (POST)', () => {
    let testUser: User;

    beforeEach(async () => {
      // 创建测试用户
      testUser = userRepository.create({
        username: 'testuser',
        email: 'test@example.com',
        password: await bcrypt.hash('password123', 12),
        nickname: 'Test User',
        phone: '13800138000',
        status: 1,
      });
      await userRepository.save(testUser);
    });

    it('should login successfully with valid credentials', async () => {
      const loginDto = {
        username: 'testuser',
        password: 'password123',
      };

      const response = await request(app.getHttpServer())
        .post('/api/auth/login')
        .send(loginDto)
        .expect(200);

      expect(response.body).toMatchObject({
        access_token: expect.any(String),
        refresh_token: expect.any(String),
        expires_in: expect.any(Number),
        user: {
          id: testUser.id,
          username: testUser.username,
          email: testUser.email,
          nickname: testUser.nickname,
        },
      });
    });

    it('should return 401 with invalid credentials', async () => {
      const loginDto = {
        username: 'testuser',
        password: 'wrongpassword',
      };

      const response = await request(app.getHttpServer())
        .post('/api/auth/login')
        .send(loginDto)
        .expect(401);

      expect(response.body.message).toBe('用户名或密码错误');
    });

    it('should return 401 when user does not exist', async () => {
      const loginDto = {
        username: 'nonexistent',
        password: 'password123',
      };

      const response = await request(app.getHttpServer())
        .post('/api/auth/login')
        .send(loginDto)
        .expect(401);

      expect(response.body.message).toBe('用户名或密码错误');
    });

    it('should return 401 when user is disabled', async () => {
      // 禁用用户
      await userRepository.update(testUser.id, { status: 0 });

      const loginDto = {
        username: 'testuser',
        password: 'password123',
      };

      const response = await request(app.getHttpServer())
        .post('/api/auth/login')
        .send(loginDto)
        .expect(401);

      expect(response.body.message).toBe('用户名或密码错误');
    });
  });

  describe('/api/auth/me (GET)', () => {
    let testUser: User;
    let accessToken: string;

    beforeEach(async () => {
      // 创建测试用户
      testUser = userRepository.create({
        username: 'testuser',
        email: 'test@example.com',
        password: await bcrypt.hash('password123', 12),
        nickname: 'Test User',
        phone: '13800138000',
        status: 1,
      });
      await userRepository.save(testUser);

      // 登录获取token
      const loginResponse = await request(app.getHttpServer())
        .post('/api/auth/login')
        .send({ username: 'testuser', password: 'password123' });

      accessToken = loginResponse.body.access_token;
    });

    it('should get current user info with valid token', async () => {
      const response = await request(app.getHttpServer())
        .get('/api/auth/me')
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200);

      expect(response.body).toMatchObject({
        id: testUser.id,
        username: testUser.username,
        email: testUser.email,
        nickname: testUser.nickname,
        userRoles: expect.any(Array),
      });

      expect(response.body).not.toHaveProperty('password');
    });

    it('should return 401 without token', async () => {
      await request(app.getHttpServer())
        .get('/api/auth/me')
        .expect(401);
    });

    it('should return 401 with invalid token', async () => {
      await request(app.getHttpServer())
        .get('/api/auth/me')
        .set('Authorization', 'Bearer invalid-token')
        .expect(401);
    });
  });

  describe('/api/auth/refresh (POST)', () => {
    let testUser: User;
    let refreshToken: string;

    beforeEach(async () => {
      // 创建测试用户
      testUser = userRepository.create({
        username: 'testuser',
        email: 'test@example.com',
        password: await bcrypt.hash('password123', 12),
        nickname: 'Test User',
        phone: '13800138000',
        status: 1,
      });
      await userRepository.save(testUser);

      // 登录获取refresh token
      const loginResponse = await request(app.getHttpServer())
        .post('/api/auth/login')
        .send({ username: 'testuser', password: 'password123' });

      refreshToken = loginResponse.body.refresh_token;
    });

    it('should refresh token successfully', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/auth/refresh')
        .send({ refresh_token: refreshToken })
        .expect(200);

      expect(response.body).toMatchObject({
        access_token: expect.any(String),
        expires_in: expect.any(Number),
      });
    });

    it('should return 401 with invalid refresh token', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/auth/refresh')
        .send({ refresh_token: 'invalid-refresh-token' })
        .expect(401);

      expect(response.body.message).toBe('刷新令牌无效');
    });

    it('should return 400 when refresh token is missing', async () => {
      await request(app.getHttpServer())
        .post('/api/auth/refresh')
        .send({})
        .expect(400);
    });
  });
});